1 /* 2 * Hunt - a framework for web and console application based on Collie using Dlang development 3 * 4 * Copyright (C) 2015-2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.http.request; 13 14 import std.exception; 15 16 import kiss.container.ByteBuffer; 17 import collie.codec.http; 18 import collie.codec.http.server.requesthandler; 19 import collie.codec.http.server.httpform; 20 import collie.utils.memory; 21 22 import hunt.http.response; 23 import hunt.http.session; 24 import hunt.http.cookie; 25 import hunt.http.exception; 26 import hunt.http.nullbuffer; 27 import hunt.routing.route; 28 import hunt.utils.time; 29 import hunt.application.application; 30 import hunt.security.acl.User; 31 32 import std.string; 33 import std.conv; 34 import std.json; 35 36 alias CreatorBuffer = Buffer delegate(HTTPMessage) nothrow; 37 alias DoHandler = void delegate(Request) nothrow; 38 39 final class Request : RequestHandler 40 { 41 this(CreatorBuffer cuffer, DoHandler handler, uint maxsize = 8 * 1024 * 1024) 42 { 43 _creatorBuffer = cuffer; 44 _handler = handler; 45 _maxBodySize = maxsize; 46 } 47 48 @property HTTPForm postForm() 49 { 50 if (_body && ( _form is null)) 51 _form = new HTTPForm(header(HTTPHeaderCode.CONTENT_TYPE),_body); 52 return _form; 53 } 54 55 @property HTTPMessage Header(){return _headers;} 56 57 @property Buffer Body(){ 58 if(_body) 59 return _body; 60 else 61 return defaultBuffer; 62 } 63 64 @property ubyte[] ubyteBody(){ 65 if(!_uBody.length){ 66 Body.rest(0); 67 Body.readAll((in ubyte[] data){ 68 _uBody ~= data; 69 }); 70 } 71 return _uBody; 72 } 73 74 @property Route route() { return _route; } 75 76 @property void route(Route value) { _route = value; } 77 78 @property User user() 79 { 80 return this._user; 81 } 82 83 @property void user(User user) 84 { 85 this._user = user; 86 } 87 88 @property string[string] mate(){return _mate;} 89 90 @property string path(){return Header.getPath;} 91 92 @property string method(){return Header.methodString;} 93 94 @property string host(){return header(HTTPHeaderCode.HOST);} 95 96 string header(HTTPHeaderCode code){ 97 return _headers.getHeaders.getSingleOrEmpty(code); 98 } 99 100 string header(string key){ 101 return _headers.getHeaders.getSingleOrEmpty(key); 102 } 103 104 bool headerExists(HTTPHeaderCode code){ 105 return _headers.getHeaders.exists(code); 106 } 107 108 bool headerExists(string key){ 109 return _headers.getHeaders.exists(key); 110 } 111 112 int headersForeach(scope int delegate(string key, string value) each){ 113 return _headers.getHeaders.opApply(each); 114 } 115 116 int headersForeach(scope int delegate(HTTPHeaderCode code,string key, string value) each){ 117 return _headers.getHeaders.opApply(each); 118 } 119 120 bool headerValueForeach(string name,scope bool delegate(string value) func){ 121 return _headers.getHeaders.forEachValueOfHeader(name,func); 122 } 123 124 bool headerValueForeach(HTTPHeaderCode code,scope bool delegate(string value) func){ 125 return _headers.getHeaders.forEachValueOfHeader(code,func); 126 } 127 128 @property string clientIp() 129 { 130 string XFF = header("X-Forwarded-For"); 131 string[] xff_arr = split(XFF,", "); 132 if(xff_arr.length > 0) 133 { 134 return xff_arr[0]; 135 } 136 string XRealIP = header("X-Real-IP"); 137 if(XRealIP.length > 0) 138 { 139 return XRealIP; 140 } 141 return clientAddress.toAddrString(); 142 } 143 @property string referer() 144 { 145 string rf = header("Referer"); 146 string[] rfarr = split(rf,", "); 147 if(rfarr.length) 148 { 149 return rfarr[0]; 150 } 151 return ""; 152 } 153 @property Address clientAddress(){return _headers.clientAddress();} 154 155 string getMate(string key,string value = null) 156 { 157 return _mate.get(key, value); 158 } 159 160 void addMate(string key, string value) 161 { 162 _mate[key] = value; 163 } 164 165 Session getSession(string sessionName = "hunt_session") 166 { 167 auto sessionId = getCookieValue(sessionName); 168 if(!sessionId.length) 169 { 170 auto _tmp = new Session(Application.getInstance().getSessionStorage()); 171 createResponse().setCookie(sessionName, _tmp.sessionId); 172 return _tmp; 173 } 174 175 return new Session(sessionId,Application.getInstance().getSessionStorage()); 176 } 177 178 @property Cookie[string] cookies() 179 { 180 if (_cookies.length == 0) 181 { 182 string cookie = header(HTTPHeaderCode.COOKIE); 183 _cookies = parseCookie(cookie); 184 } 185 186 return _cookies; 187 } 188 189 private Cookie getCookie(string key) 190 { 191 return cookies.get(key,null); 192 } 193 194 string getCookieValue(string key) 195 { 196 auto cookie = this.getCookie(key); 197 if(cookie is null) 198 { 199 return ""; 200 } 201 return cookie.value; 202 } 203 204 @property JSONValue json() 205 { 206 if(_json == JSONValue.init){ 207 _json = parseJSON(cast(string)ubyteBody()); 208 } 209 return _json; 210 } 211 auto json(T)(string key) 212 { 213 import std.traits; 214 auto obj = (key in (json().objectNoRef)); 215 if(obj is null) 216 return T.init; 217 static if(isIntegral!(T)) 218 return cast(T)((*obj).integer); 219 else static if(is(T == string)) 220 return (*obj).str; 221 else static if(is(FloatingPointTypeOf!T X)) 222 return cast(T)((*obj).floating); 223 else static if(is(T == bool)){ 224 if(obj.type == JSON_TYPE.TRUE) 225 return true; 226 else if(obj.type == JSON_TYPE.FALSE) 227 return false; 228 else { 229 throw new Exception("json error"); 230 return false; 231 } 232 } else { 233 return (*obj); 234 } 235 } 236 ///get queries 237 @property string[string] queries() 238 { 239 return _headers.queryParam(); 240 } 241 /// get a query 242 T get(T = string)(string key, T v = T.init) 243 { 244 import std.conv; 245 auto tmp = queries(); 246 if(tmp is null) 247 { 248 return v; 249 } 250 auto _v = tmp.get(key, ""); 251 if(_v.length) 252 { 253 return to!T(_v); 254 } 255 return v; 256 } 257 258 /// get a post 259 T post(T = string)(string key, T v = T.init) 260 { 261 import std.conv; 262 auto form = postForm(); 263 if(form is null) return v; 264 auto _v = postForm.getFromValue(key); 265 if(_v.length) 266 { 267 return to!T(_v); 268 } 269 return v; 270 } 271 // alias _req this; 272 273 /// GET FILE , if return NULL , file is null 274 auto file(string key) 275 { 276 return postForm.getFileValue(key); 277 } 278 279 @property ref string[string] materef() {return _mate;} 280 281 Response createResponse() 282 { 283 if(_error != HTTPErrorCode.NO_ERROR) 284 throw new CreateResponseException("http error is : " ~ to!string(_error)); 285 if(_res is null) { 286 _res = new Response(_downstream); 287 } 288 return _res; 289 } 290 291 @property void action(string value) 292 { 293 _action = value; 294 } 295 296 @property string action() 297 { 298 return _action; 299 } 300 301 protected: 302 override void onBody(const ubyte[] data) nothrow { 303 collectException((){ 304 if(fristBody){ 305 _body = _creatorBuffer(_headers); 306 fristBody = false; 307 if(_body is null) { 308 onError(HTTPErrorCode.FRAME_SIZE_ERROR); 309 return; 310 } 311 } 312 if(_body) { 313 _body.write(data); 314 if(_body.length > _maxBodySize){ 315 onError(HTTPErrorCode.FRAME_SIZE_ERROR); 316 gcFree(_body); 317 _body = null; 318 } 319 } 320 }()); 321 } 322 323 override void onEOM() nothrow { 324 if(_error == HTTPErrorCode.NO_ERROR) 325 _handler(this); 326 } 327 328 override void requestComplete() nothrow { 329 collectException((){ 330 _error = HTTPErrorCode.STREAM_CLOSED; 331 import collie.utils.memory; 332 if(_body)gcFree(_body); 333 if(_headers)gcFree(_headers); 334 if(_res)gcFree(_res); 335 }()); 336 } 337 338 override void onResquest(HTTPMessage headers) nothrow { 339 _headers = headers; 340 } 341 342 override void onError(HTTPErrorCode code) nothrow { 343 collectException((){ 344 scope(exit) { 345 _downstream = null; 346 } 347 _error = code; 348 if(_error == HTTPErrorCode.REMOTE_CLOSED) 349 return; 350 if(_res is null){ 351 _res = new Response(_downstream); 352 } 353 if(_error == HTTPErrorCode.TIME_OUT){ 354 _res.setHttpStatusCode(408); 355 } else if(_error == HTTPErrorCode.FRAME_SIZE_ERROR){ 356 _res.setHttpStatusCode(429); 357 } else { 358 _res.setHttpStatusCode(502); 359 } 360 _res.done(); 361 //_res.clear(); 362 }()); 363 } 364 365 private: 366 User _user; 367 Route _route; 368 string[string] _mate; 369 Cookie[string] _cookies; 370 Buffer _body; 371 JSONValue _json; 372 ubyte[] _uBody; 373 HTTPMessage _headers; 374 HTTPForm _form; 375 Response _res; 376 HTTPErrorCode _error = HTTPErrorCode.NO_ERROR; 377 CreatorBuffer _creatorBuffer; 378 uint _maxBodySize; 379 DoHandler _handler; 380 bool fristBody = true; 381 string _action; 382 }